use anyhow::Result;
use git2::{BranchType, Repository};
use log::info;
use tokio::sync::oneshot;
use tokio::task;

use super::{CommitInfo, format_git_time};

/// 切换到指定分支
pub async fn checkout_branch(
    branch_name: &str,
    force: bool, // 添加 force 参数，以匹配 manager.rs 中的调用
    mut cancel_rx: Option<oneshot::Receiver<()>>,
) -> Result<()> {
    let branch_name = branch_name.to_string();

    task::spawn_blocking(move || {
        // 检查任务是否被取消
        if let Some(rx) = &mut cancel_rx {
            // 使用 &mut 而非 ref mut
            if rx.try_recv().is_ok() {
                return Err(anyhow::anyhow!("Operation cancelled"));
            }
        }

        let repo = Repository::open(".")?;
        let branch = repo.find_branch(&branch_name, BranchType::Local)?;
        let branch_ref = branch.get();
        let commit = repo.find_commit(
            branch_ref
                .target()
                .ok_or_else(|| anyhow::anyhow!("Invalid branch reference"))?,
        )?;

        // 创建检出选项
        let mut checkout_builder = git2::build::CheckoutBuilder::new();

        if force {
            // 使用 force 参数
            checkout_builder.force();
        }

        // 检出到指定提交
        repo.checkout_tree(commit.as_object(), Some(&mut checkout_builder))?;

        // 更新 HEAD 引用
        repo.set_head(&format!("refs/heads/{}", branch_name))?;

        Ok(())
    })
    .await?
}



/// 合并指定的提交到当前分支
pub async fn merge_commits(
    _source_branch: &str,
    commit_ids: &[String],
    mut cancel_rx: Option<oneshot::Receiver<()>>,
) -> Result<()> {
    let commit_ids = commit_ids.to_vec();
    info!("Merging the following commit IDs: {:?}", commit_ids);

    task::spawn_blocking(move || {
        // 检查任务是否被取消
        if let Some(rx) = &mut cancel_rx {
            if rx.try_recv().is_ok() {
                return Err(anyhow::anyhow!("Operation cancelled"));
            }
        }

        let repo = Repository::open(".")?;

        // 反转提交 ID 顺序，从最早的提交开始应用
        let mut reversed_commit_ids = commit_ids.clone();
        reversed_commit_ids.reverse();

        // 记录成功和失败的提交
        let mut succeeded_commits = Vec::new();
        let mut failed_commits = Vec::new();

        // 确保工作区干净
        repo.checkout_head(Some(
            &mut git2::build::CheckoutBuilder::new().force(),
        ))?;

        // 对每个指定的提交进行合并
        for commit_id in &reversed_commit_ids {
            info!("Processing commit: {}", commit_id);

            // 查找提交
            let oid = match git2::Oid::from_str(commit_id) {
                Ok(oid) => oid,
                Err(e) => {
                    failed_commits.push((commit_id.clone(), format!("无效提交ID: {}", e)));
                    continue;
                }
            };

            let commit = match repo.find_commit(oid) {
                Ok(commit) => commit,
                Err(e) => {
                    failed_commits.push((commit_id.clone(), format!("找不到提交: {}", e)));
                    continue;
                }
            };

            // 获取当前HEAD的最新状态
            let head = repo.head()?;
            let head_commit = repo.find_commit(
                head.target()
                    .ok_or_else(|| anyhow::anyhow!("Invalid HEAD reference"))?,
            )?;
            info!("Current HEAD commit: {:?}", head_commit.id());

            // 尝试使用更简单的方法：使用git命令进行cherry-pick
            // 首先创建一个临时索引
            let mut index = repo.index()?;

            // 直接使用cherry-pick API
            let cherrypick_result = repo.cherrypick(&commit, None);
            if let Err(e) = cherrypick_result {
                failed_commits.push((commit_id.clone(), format!("Cherry-pick失败: {}", e)));
                // 确保工作区重置到干净状态
                repo.checkout_head(Some(&mut git2::build::CheckoutBuilder::new().force()))?;
                continue;
            }

            // 如果有冲突，则中止此提交的合并
            if repo.index()?.has_conflicts() {
                failed_commits.push((commit_id.clone(), "Cherry-pick导致冲突".to_string()));
                // 重置工作区到干净状态
                repo.checkout_head(Some(&mut git2::build::CheckoutBuilder::new().force()))?;
                continue;
            }

            // 没有冲突，提交更改
            let tree_id = index.write_tree()?;
            let tree = repo.find_tree(tree_id)?;

            let signature = repo.signature()?;
            let message = commit.message().unwrap_or("No message").to_string();

            // 创建提交
            match repo.commit(
                Some("HEAD"),
                &signature,
                &signature,
                &message,
                &tree,
                &[&head_commit],
            ) {
                Ok(new_commit_id) => {
                    succeeded_commits.push(commit_id.clone());
                    info!("Successfully created new commit: {:?}", new_commit_id);

                    // 确保工作区与HEAD同步
                    repo.checkout_head(Some(
                        &mut git2::build::CheckoutBuilder::new().force(),
                    ))?;
                },
                Err(e) => {
                    failed_commits.push((commit_id.clone(), format!("无法创建提交: {}", e)));
                    // 重置工作区
                    repo.checkout_head(Some(
                        &mut git2::build::CheckoutBuilder::new().force(),
                    ))?;
                }
            }
        }

        // 返回合并结果
        if !failed_commits.is_empty() {
            let error_msg = failed_commits
                .iter()
                .map(|(id, msg)| format!("提交 {} 失败: {}", id, msg))
                .collect::<Vec<_>>()
                .join("\n");

            if succeeded_commits.is_empty() {
                return Err(anyhow::anyhow!("所有提交都失败: {}", error_msg));
            } else {
                return Err(anyhow::anyhow!(
                    "部分提交成功 ({} 成功, {} 失败):\n{}",
                    succeeded_commits.len(),
                    failed_commits.len(),
                    error_msg
                ));
            }
        }

        Ok(())
    })
    .await?
}

/// 获取两个分支之间的差异提交
pub async fn get_branch_diff_commits(
    source_branch: &str,
    target_branch: &str,
) -> Result<Vec<CommitInfo>> {
    let source_branch = source_branch.to_string();
    let target_branch = target_branch.to_string();

    task::spawn_blocking(move || {
        let repo = Repository::open(".")?;

        // 获取源分支和目标分支引用
        let source_branch_obj = repo.find_branch(&source_branch, BranchType::Local)?;
        let source_ref = source_branch_obj.get();

        let target_branch_obj = repo.find_branch(&target_branch, BranchType::Local)?;
        let target_ref = target_branch_obj.get();

        // 获取源分支和目标分支的最新提交
        let source_commit = repo.find_commit(
            source_ref
                .target()
                .ok_or_else(|| anyhow::anyhow!("Invalid branch reference"))?,
        )?;
        let target_commit = repo.find_commit(
            target_ref
                .target()
                .ok_or_else(|| anyhow::anyhow!("Invalid branch reference"))?,
        )?;

        // 找到两个分支的合并基础，这是非常重要的一步
        let base_commit_oid = repo.merge_base(source_commit.id(), target_commit.id())?;

        let mut revwalk = repo.revwalk()?;
        revwalk.set_sorting(git2::Sort::TOPOLOGICAL | git2::Sort::TIME)?;

        // 只关注源分支上从合并基础到当前HEAD的提交
        revwalk.push(source_commit.id())?;
        revwalk.hide(base_commit_oid)?;

        // 还需要查找目标分支上的提交，以便排除已经被cherry-pick的提交
        let mut target_commits = std::collections::HashSet::new();
        let mut target_walk = repo.revwalk()?;
        target_walk.push(target_commit.id())?;
        target_walk.hide(base_commit_oid)?;

        // 收集目标分支上所有提交的patch ID
        // patch ID可以识别相同更改的提交，即使它们有不同的commit hash
        for oid_result in target_walk {
            let oid = oid_result?;
            let commit = repo.find_commit(oid)?;

            // 计算提交的patch ID（这需要考虑提交的更改内容，而不仅仅是提交ID）
            if let Ok(patch_id) = calculate_patch_id(&repo, &commit) {
                target_commits.insert(patch_id);
            }
        }

        let mut diff_commits = Vec::new();

        // 遍历源分支上的差异提交
        for oid_result in revwalk {
            let oid = oid_result?;
            let commit = repo.find_commit(oid)?;

            // 计算当前提交的patch ID
            if let Ok(patch_id) = calculate_patch_id(&repo, &commit) {
                // 如果这个提交的patch ID已经在目标分支上，那么跳过它
                if target_commits.contains(&patch_id) {
                    continue;
                }
            }

            // 这个提交是真正的差异提交
            let commit_info = CommitInfo {
                id: commit.id().to_string(),
                message: commit.message().unwrap_or("").to_string(),
                author: commit.author().name().unwrap_or("Unknown").to_string(),
                time: format_git_time(&commit.time()),
            };

            diff_commits.push(commit_info);

            // 限制提交数量
            if diff_commits.len() >= 100 {
                break;
            }
        }

        Ok(diff_commits)
    })
    .await?
}

/// 计算提交的patch ID
fn calculate_patch_id(repo: &Repository, commit: &git2::Commit) -> Result<git2::Oid> {
    // 检查提交是否有父提交
    if commit.parent_count() > 0 {
        let parent = commit.parent(0)?;

        // 获取提交与其父提交之间的差异
        let diff = repo.diff_tree_to_tree(
            Some(&parent.tree()?),
            Some(&commit.tree()?),
            Some(&mut git2::DiffOptions::new())
        )?;

        // 计算差异的patch ID
        Ok(diff.patchid(None)?)
    } else {
        // 如果没有父提交，那么就直接使用提交ID作为patch ID
        Ok(commit.id())
    }
}
